﻿#include <iostream>
#include <cstdint>
#include <string>
#include <set>
using namespace std;

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
using boost::multi_index_container;
using namespace boost::multi_index;

class Course
{
public:
    uint32_t id;
    uint32_t hour;
    std::string name;

    // 课程枚举
    enum E_Type
    {
        Begin,
        CPP = Begin,   // C++
        English,       // 英语
        Maths,         // 数学
        Computer,      // 计算机
        DataStructure, // 数据结构
        End,
        Num = End,
    };

    // 获取课程
    static const Course *Get(E_Type type)
    {
        // 预定义一些课程
        static Course s_courses[Course::Num] = {
            Course(CPP, 80, "C++程序设计"),
            Course(English, 60, "大学英语"),
            Course(Maths, 45, "离散数学"),
            Course(Computer, 50, "计算机组成原理"),
            Course(DataStructure, 60, "数据结构")};

        if (type < Begin || type >= End)
        {
            return nullptr;
        }
        return &s_courses[type];
    }

    friend std::ostream &operator<<(std::ostream &out, const Course &course)
    {
        out << "{";
        out << "\"id\":" << course.id;
        out << ",\"hour\":" << course.hour;
        out << ",\"name\":\"" << course.name << "\"";
        out << "}";
        return out;
    }

private:
    Course(uint32_t id, uint32_t hour, const char *szName)
        : id(id), hour(hour), name(szName){};
};

// 学生
class Student
{
public:
    uint64_t id;                         // 学号
    std::string name;                    // 姓名
    uint32_t age;                        // 年龄
    std::set<const Course *> courseList; // 主修课程表

    static Student Create(const std::string &name, uint32_t age)
    {
        static uint64_t s_nextID = 0;
        return Student(++s_nextID, name, age);
    };

    struct IndexID
    {
    }; // 索引-学号
    struct IndexName
    {
    }; // 索引-姓名
    struct IndexAge
    {
    }; // 索引-年龄

    friend std::ostream &operator<<(std::ostream &out, const Student &stu)
    {
        out << "{";
        out << "\"id\":" << stu.id;
        out << ",\"name\":\"" << stu.name << "\"";
        out << ",\"age\":" << stu.age;
        out << ",\"courseList\":{";
        for (auto &&it = stu.courseList.begin(), end = stu.courseList.end(); it != end; it++)
        {
            out << **it << ",";
        }
        out << "}}";
        return out;
    }

private:
    Student(uint64_t id, const char *szName, uint32_t age)
        : id(id), name(szName), age(age){};

    Student(uint64_t id, const std::string &name, uint32_t age)
        : id(id), name(name), age(age){};

    Student(uint64_t id, std::string &&name, uint32_t age)
        : id(id), name(std::move(name)), age(age){};
};

using StudentContainer = boost::multi_index_container<
    Student,
    indexed_by<
        // 学号是唯一值的索引
        ordered_unique<tag<Student::IndexID>, BOOST_MULTI_INDEX_MEMBER(Student, uint64_t, id)>,
        // 姓名是非唯一值的索引
        ordered_non_unique<tag<Student::IndexName>, BOOST_MULTI_INDEX_MEMBER(Student, std::string, name)>,
        // 年龄是非唯一值的索引
        ordered_non_unique<tag<Student::IndexAge>, BOOST_MULTI_INDEX_MEMBER(Student, uint32_t, age)>>>;

template <typename Tag, typename MultiIndexContainer>
void print_out_by(const MultiIndexContainer &s)
{
    /* obtain a reference to the index tagged by Tag */

    const typename index<MultiIndexContainer, Tag>::type &i = get<Tag>(s);

    using value_type = typename MultiIndexContainer::value_type;

    /* dump the elements of the index to cout */

    std::copy(i.begin(), i.end(), std::ostream_iterator<value_type>(std::cout));
}

int main(int argc, char *argv[])
{
    StudentContainer students;

    // 插入一些数据
    Student stu1 = Student::Create("张三", 19);
    stu1.courseList.emplace(Course::Get(Course::CPP));
    stu1.courseList.emplace(Course::Get(Course::English));
    students.insert(stu1);

    Student stu2 = Student::Create("李四", 18);
    stu2.courseList.emplace(Course::Get(Course::CPP));
    stu2.courseList.emplace(Course::Get(Course::DataStructure));
    stu2.courseList.emplace(Course::Get(Course::Computer));
    students.insert(stu2);

    Student stu3 = Student::Create("王五", 21);
    stu3.courseList.emplace(Course::Get(Course::English));
    stu3.courseList.emplace(Course::Get(Course::Maths));
    students.insert(stu3);

    Student stu4 = Student::Create("张三", 18);
    stu4.courseList.emplace(Course::Get(Course::Computer));
    stu4.courseList.emplace(Course::Get(Course::Maths));
    students.insert(stu4);

    // 按学号排序输出序列
    print_out_by<Student::IndexID>(students);
    cout << endl;

    // 按名字排序输出序列
    print_out_by<Student::IndexName>(students);
    cout << endl;

    // 按年龄排序输出序列
    print_out_by<Student::IndexAge>(students);
    cout << endl;

    // 用名字作为索引
    auto &indexOfName = students.get<Student::IndexName>();

    // 查找名叫张三的人的下界
    auto &&itL = indexOfName.lower_bound("张三");

    // 查找名叫张三的人的上界
    auto &&itU = indexOfName.upper_bound("张三");

    // 遍历输出所有名叫“张三”的学生信息
    while (itL != itU)
    {
        cout << *itL << endl;
        ++itL;
    }

    // 查找名叫李四的人
    auto &&it = indexOfName.find("李四");
    // 找到了？
    if (it != indexOfName.end())
    {
        // 修改部分带索引的资料
        Student temp = *it;
        // 没问题可以修改，age是不唯一的索引
        temp.age = 20;
        // 天哪,竟然要修改学号？
        // temp.id = 1;
        // 将it指向的元素替换为stuTemp，ret为false
        bool ret = indexOfName.replace(it, temp);
    }

    return 0;
}
